package plus.easydo.xxl.job.auto.config;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.xxl.job.core.handler.annotation.XxlJob;
import plus.easydo.xxl.job.request.XxlJobRequestFactory;
import plus.easydo.xxl.job.config.ScheduleTypeEnum;
import plus.easydo.xxl.job.config.XxlJobPlusProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import plus.easydo.xxl.job.auto.annotion.AutoRegister;
import plus.easydo.xxl.job.model.ExecutorInfo;
import plus.easydo.xxl.job.model.SchedulerInfo;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 自动注册执行器和带有注解的任务
 *
 * @author laoyu
 * @date 2023/5/21
 */
@Configuration
public class AutoRegisterConfig implements CommandLineRunner {

    Logger log = LoggerFactory.getLogger(AutoRegisterConfig.class);

    @Autowired
    private XxlJobRequestFactory xxlJobRequestFactory;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private XxlJobPlusProperties xxlJobPlusProperties;

    @Override
    public void run(String... args) throws Exception {

        // 1.获取到相关注解后，需要不需要异步进行注册
        // 2.报错是应该打印日志后,不作注册调度就行,还是项目停止运行
        Map<XxlJob, AutoRegister> map = MapUtil.newHashMap(4);

        String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = applicationContext.getBean(beanDefinitionName);
            Map<Method, XxlJob> annotatedJob;
            Map<Method, AutoRegister> annotatedScheduler;
            try {
                annotatedJob = MethodIntrospector.selectMethods(bean.getClass(),
                        (MethodIntrospector.MetadataLookup<XxlJob>) method ->
                                AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class));

                annotatedScheduler = MethodIntrospector.selectMethods(bean.getClass(),
                        (MethodIntrospector.MetadataLookup<AutoRegister>) method ->
                                AnnotatedElementUtils.findMergedAnnotation(method, AutoRegister.class));

            } catch (Exception ex) {
                log.warn("xxl-job auto register resolve error for bean[{}].", beanDefinitionName, ex);
                log.warn("[{}]类获取注解异常, 停止自动添加执行器和调度器", beanDefinitionName);
                return;
            }

            for (Map.Entry<Method, AutoRegister> methodAutoSchedulerEntry : annotatedScheduler.entrySet()) {
                Method method = methodAutoSchedulerEntry.getKey();
                AutoRegister autoRegister = methodAutoSchedulerEntry.getValue();
                XxlJob xxlJob = annotatedJob.get(method);
                if (Objects.isNull(xxlJob)) {
                    log.warn("[{}]类中没有添加XxlJob注解,不能将其添加调度器", beanDefinitionName);
                    continue;
                }
                map.put(xxlJob, autoRegister);
            }
        }
        // 信息注册
        this.registerCheck(map);
    }

    /**
     * 注册前校验
     *
     * @param map xxlJob和autoRegister注解内容
     */
    private void registerCheck(Map<XxlJob, AutoRegister> map) {
        if (map.isEmpty()) {
            return;
        }
        String username = xxlJobPlusProperties.getAdmin().getUsername();
        String password = xxlJobPlusProperties.getAdmin().getPassword();
        // 用户名密码非空校验
        if (CharSequenceUtil.isBlank(username) || CharSequenceUtil.isBlank(password)) {
            log.warn("xxl-job账号或密码为空,停止自动添加执行器和调度器");
            return;
        }
        String title = xxlJobPlusProperties.getExecutor().getTitle();
        // 注册器名称校验
        if (CharSequenceUtil.isBlank(title)) {
            log.warn("xxl-job执行器名称为空,停止自动添加执行器和调度器");
            return;
        }
        // 注册执行器
        try {
            registerExecutor(map);
        } catch (Exception e) {
            log.warn("xxl-job自动添加异常,停止自动添加", e);
        }
    }

    /**
     * 注册执行器
     *
     * @param map xxlJob和autoRegister注解内容
     */
    private void registerExecutor(Map<XxlJob, AutoRegister> map) {
        // 获取执行器列表
        // 获取当前注册名的信息列表
        List<ExecutorInfo> executorInfos = xxlJobRequestFactory.getRequestTarget().executorList();
        String appName = xxlJobPlusProperties.getExecutor().getAppname();
        if (executorInfos.size() > 1) {
            // 存在多个,停止注册
            log.warn("xxl-job中存在多个[{}]执行器,已停止自动注册功能", appName);
            return;
        } else if (executorInfos.size() == 1) {
            // 该项目执行器已注册,跳过。
            log.info("++++++++++[{}]执行器已存在++++++++++", appName);
        } else {
            // 不存在该执行器,注册
            xxlJobRequestFactory.getRequestTarget().executorRegister();
            log.info("++++++++++[{}]执行器添加成功++++++++++", appName);
        }
        // 注册调度器
        registerScheduler(executorInfos, map);
    }

    /**
     * 注册调度器
     *
     * @param executorInfos 执行器信息
     * @param map xxlJob和autoRegister注解内容
     */
    private void registerScheduler(List<ExecutorInfo> executorInfos, Map<XxlJob, AutoRegister> map) {
        // 获取执行器ID
        int jobGroup;
        if (executorInfos.size() == 1) {
            // 已有注册器
            jobGroup = executorInfos.get(0).getId();
        } else {
            // 刚注册, 需要获取对应的执行器id
            executorInfos = xxlJobRequestFactory.getRequestTarget().executorList();
            String appName = xxlJobPlusProperties.getExecutor().getAppname();
            if (executorInfos.size() > 1) {
                // 存在多个,停止注册
                log.warn("xxl-job中存在多个[{}]执行器,已停止自动注册功能", appName);
                return;
            } else if (executorInfos.isEmpty()) {
                log.warn("[{}]执行器注册异常,未获取到上文所注册的执行器", appName);
                throw ExceptionUtil.wrapRuntime(appName + "执行器自动注册异常, 未获取到上文所注册的执行器");
            } else {
                jobGroup = executorInfos.get(0).getId();
            }
        }

        // 获取该id对应执行器下的调度器
        List<SchedulerInfo> schedulerInfos = xxlJobRequestFactory.getRequestTarget().schedulerList(jobGroup);
        List<String> executorHandlerList = schedulerInfos.stream()
                .map(SchedulerInfo::getExecutorHandler).collect(Collectors.toList());

        for (Map.Entry<XxlJob, AutoRegister> xxlJobAutoRegisterEntry : map.entrySet()) {
            XxlJob xxlJob = xxlJobAutoRegisterEntry.getKey();
            String executorHandler = xxlJob.value();
            if (executorHandlerList.contains(executorHandler)) {
                log.info("++++++++++[{}]调度器已存在++++++++++", executorHandler);
                continue;
            }
            AutoRegister autoRegister = xxlJobAutoRegisterEntry.getValue();
            SchedulerInfo schedulerInfo = new SchedulerInfo();
            schedulerInfo.setJobGroup(jobGroup);
            schedulerInfo.setJobDesc(autoRegister.desc());
            schedulerInfo.setExecutorHandler(executorHandler);
            schedulerInfo.setAlarmEmail(autoRegister.alarmEmail());
            schedulerInfo.setAuthor(autoRegister.author());
            schedulerInfo.setChildJobId(autoRegister.childJobId());
            schedulerInfo.setExecutorBlockStrategy(autoRegister.executorBlockStrategy().getStrategy());
            schedulerInfo.setExecutorRouteStrategy(autoRegister.executorRouteStrategy().getStrategy());
            schedulerInfo.setGlueType(autoRegister.glueType().getCode());
            schedulerInfo.setExecutorTimeout(autoRegister.executorTimeout());
            schedulerInfo.setExecutorFailRetryCount(autoRegister.executorFailRetryCount());
            schedulerInfo.setExecutorParam(autoRegister.executorParam());
            schedulerInfo.setGlueRemark(autoRegister.glueRemark());
            schedulerInfo.setGlueSource(autoRegister.glueSource());
            schedulerInfo.setMisfireStrategy(autoRegister.misfireStrategy().getCode());
            generateSchedulerConfig(autoRegister,schedulerInfo);
            log.debug("SchedulerInfo:{}", schedulerInfo);
            xxlJobRequestFactory.getRequestTarget().schedulerRegister(schedulerInfo);
            log.info("++++++++++[{}]调度器添加完成++++++++++", executorHandler);
        }
    }

    /**
     * 根据调度方式设置调度配置
     *
     * @param autoRegister autoRegister
     * @param schedulerInfo schedulerInfo
     * @author laoyu
     * @date 2021/10/27
     */
    private void generateSchedulerConfig(AutoRegister autoRegister, SchedulerInfo schedulerInfo){
        String scheduleType = autoRegister.scheduleType().getType();
        schedulerInfo.setScheduleType(autoRegister.scheduleType().getType());
        if(scheduleType.equals(ScheduleTypeEnum.CRON.getType())){
            schedulerInfo.setScheduleConf(autoRegister.scheduleConf());
            schedulerInfo.setScheduleConfCron(autoRegister.scheduleConf());
            schedulerInfo.setCronGenDisplay(autoRegister.scheduleConf());
        }
        if(scheduleType.equals(ScheduleTypeEnum.FIX_RATE.getType())){
            schedulerInfo.setScheduleConf(autoRegister.scheduleConf());
            schedulerInfo.setScheduleConfFixRate(autoRegister.scheduleConf());
            schedulerInfo.setScheduleConfFixDelay(autoRegister.scheduleConf());
        }
    }

}
